package com.santoker.baseservice.util;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.santoker.baseservice.annotation.HttpParameter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Set;

@Slf4j
public class OkHttpHelper {

    public static final int HTTP_SUCCESS_CODE = 200;

    public static final int HTTP_FORBIDDEN_CODE = 200;

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    public static final MediaType FORM = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");

    public static final MediaType TEXT_HTML = MediaType.parse("text/html; charset=utf-8");

    private static final OkHttpClient client = new OkHttpClient();

    public static OkHttpClient getOkHttpClient() {
        return client;
    }

    public static Response get(String url, Map<String, Object> params) {
        return get(url + parseParams(params));
    }

    public static Response get(String url) {
        log.info("send get request, url: {}", url);
        try {
            Request request = new Request.Builder()
                    .url(url)
                    .build();
            Response response = client.newCall(request).execute();
            return response;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static Response delete(String url, Map<String, Object> params) {
        return delete(url, parseParams(params), null);
    }

    public static Response delete(String url, Map<String, Object> params, Map<String, String> headers) {
        return delete(url, parseParams(params), headers);
    }

    public static Response delete(String url, String params, Map<String, String> headers) {
        Request.Builder requestBuilder = new Request.Builder()
                .url(url + params)
                .delete();
        if (headers != null && headers.size() > 0) {
            requestBuilder.headers(parseHeaders(headers));
        }

        try {
            Response response = client.newCall(requestBuilder.build()).execute();

            log.info("delete response from url: {}, params: {}, response: {}",
                    url, params, response.toString());
            return response;
        } catch (IOException e) {
            log.error("delete request failed, url: {}, msg: {}", url, e.getMessage());
            e.printStackTrace();
        }

        return null;
    }

    public static Response post(String url, Map<String, Object> params, Object postParams) {
        return post(url, params, postParams, JSON);
    }

    public static Response post(String url, Map<String, Object> params, String postParams, MediaType mediaType) {
        return post(url + parseParams(params), postParams, mediaType, null);
    }

    public static Response post(String url, Map<String, Object> params, Object postParams, MediaType mediaType) {
        RequestBody body = RequestBody.create(mediaType, parsePostParams(postParams));
        return post(url + parseParams(params), parsePostParams(postParams), mediaType, null);
    }

    public static Response post(String url, Map<String, Object> params, Object postParams, MediaType mediaType, Map<String, String> headers) {
        RequestBody body = RequestBody.create(mediaType, parsePostParams(postParams));
        return post(url + parseParams(params), parsePostParams(postParams), mediaType, headers);
    }

    public static Response post(String url, Map<String, Object> params, String postParams, MediaType mediaType, Map<String, String> headers) {
        RequestBody body = RequestBody.create(mediaType, postParams);
        return post(url + parseParams(params), postParams, mediaType, headers);
    }

//    public static String postForm(String url, Map<String, Object> params, Object postParams) {
//        RequestBody body = RequestBody.create(FORM, parseGetParams(postParams));
//        return post(url + parseParams(params), body);
//    }
//
//    public static String postForm(String url, Map<String, Object> params, Map<String, Object> postParams) {
//        RequestBody body = RequestBody.create(FORM, parseGetParams(postParams));
//        return post(url + parseParams(params), body);
//    }

    public static Response put(String url, Map<String, Object> params, Object postParams) {
        return put(url, params, postParams, JSON);
    }

    public static Response put(String url, Map<String, Object> params, Object postParams, MediaType mediaType) {
        RequestBody body = RequestBody.create(mediaType, parsePostParams(postParams));
        return put(url + parseParams(params), parsePostParams(postParams), mediaType, null);
    }

    public static Response put(String url, Map<String, Object> params, Object postParams, MediaType mediaType, Map<String, String> headers) {
        RequestBody body = RequestBody.create(mediaType, parsePostParams(postParams));
        return put(url + parseParams(params), parsePostParams(postParams), mediaType, headers);
    }

    public static Response post(String url, String postParams, MediaType mediaType, Map<String, String> headers) {
        RequestBody body = RequestBody.create(mediaType, postParams);
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .post(body);
        if (headers != null && headers.size() > 0) {
            requestBuilder.headers(parseHeaders(headers));
        }

        try {
            Response response = client.newCall(requestBuilder.build()).execute();
            log.info("post response from url: {}, requestBody: {}, response: {}",
                    url, postParams, response.toString());
            return response;
        } catch (IOException e) {
            log.error("post request failed, url: {}, body: {}, msg: {}", url, body, e.getMessage());
            e.printStackTrace();
        }

        return null;
    }


    public static Response put(String url, String postParams, MediaType mediaType, Map<String, String> headers) {
        RequestBody body = RequestBody.create(mediaType, postParams);
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .put(body);
        if (headers != null && headers.size() > 0) {
            requestBuilder.headers(parseHeaders(headers));
        }

        try {
            Response response = client.newCall(requestBuilder.build()).execute();
            log.info("put response from url: {}, requestBody: {}, response: {}",
                    url, postParams, response.toString());
            return response;
        } catch (IOException e) {
            log.error("put request failed, url: {}, body: {}, msg: {}", url, body, e.getMessage());
            e.printStackTrace();
        }

        return null;
    }

    public static String parseParams(Map<String, Object> params) {
        StringBuilder builder = new StringBuilder();
        if (params == null || params.size() == 0) {
            return "";
        }
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            builder.append("&")
                    .append(entry.getKey())
                    .append("=")
                    .append(entry.getValue().toString());
        }

        if (builder.indexOf("&") == 0) {
            builder.replace(0, 1, "?");
        }
        return builder.toString();
    }

    public static <T> String parsePostXMLParams(Object params) {
        Map<String, Object> result = Maps.newHashMap();
        Set<Field> fields = ReflectionUtil.getClassAllFields(params.getClass());
        StringBuilder builder = new StringBuilder();
        builder.append("<xml>");

        try {
            for (Field field : fields) {
                HttpParameter httpParamAnnotation = field.getAnnotation(HttpParameter.class);
                if (httpParamAnnotation != null) {
                    String paramName = httpParamAnnotation.name(), paramValue;
                    field.setAccessible(true);
                    if (Strings.isNullOrEmpty(paramName)) {
                        paramName = field.getName();
                    }
//                    if (field.get(params) != null && !Strings.isNullOrEmpty(paramValue = field.get(params).toString())) {
//                        builder.append("<" + paramName + ">" + paramValue + "</" + paramName + ">");
//                    }
                    if (field.get(params) != null) {
                        builder.append("<" + paramName + ">" + field.get(params).toString() + "</" + paramName + ">");
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        builder.append("</xml>");

        return builder.toString();
    }

    public static <T> String parsePostParams(Object params) {
        Map<String, Object> result = Maps.newHashMap();
        Set<Field> fields = ReflectionUtil.getClassAllFields(params.getClass());

        try {
            for (Field field : fields) {
                HttpParameter httpParamAnnotation = field.getAnnotation(HttpParameter.class);
                if (httpParamAnnotation != null) {
                    String paramName = httpParamAnnotation.name();
                    field.setAccessible(true);
                    if (Strings.isNullOrEmpty(paramName)) {
                        paramName = field.getName();
                    }
                    result.put(paramName, field.get(params));
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return JSONUtil.jsonEncode(result);
    }

    public static <T> String parseGetParams(Object params, boolean urlEncode) {
        StringBuilder builder = new StringBuilder();
        Set<Field> fields = ReflectionUtil.getClassAllFields(params.getClass());

        try {
            for (Field field : fields) {
                HttpParameter httpParamAnnotation = field.getAnnotation(HttpParameter.class);
                if (httpParamAnnotation != null) {
                    String paramName = httpParamAnnotation.name();
                    if (Strings.isNullOrEmpty(paramName)) {
                        paramName = field.getName();
                    }
                    field.setAccessible(true);
                    if (field.get(params) != null) {
                        String val = urlEncode ? urlEncode(field.get(params).toString())
                                : field.get(params).toString();
                        builder.append("&")
                                .append(paramName)
                                .append("=")
                                .append(val);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        if (builder.indexOf("&") == 0) {
            builder.replace(0, 1, "");
        }
        return builder.toString();
    }

    public static <T> String parseGetParamsInternetion(Object params, boolean urlEncode) {
        StringBuilder builder = new StringBuilder();
        Set<Field> fields = ReflectionUtil.getClassAllFields(params.getClass());

        try {
            for (Field field : fields) {
                HttpParameter httpParamAnnotation = field.getAnnotation(HttpParameter.class);
                if (httpParamAnnotation != null) {
                    String paramName = httpParamAnnotation.name();
                    if (Strings.isNullOrEmpty(paramName)) {
                        paramName = field.getName();
                    }
                    field.setAccessible(true);
                    if (field.get(params) != null) {
                        String val = urlEncode ? urlEncode(field.get(params).toString())
                                : field.get(params).toString();
                        builder.append("&")
                                .append(paramName)
                                .append("=")
                                .append(val);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        if (builder.indexOf("&") == 0) {
            builder.replace(0, 1, "");
        }
        String builderString = builder.toString();

        return builderString.replace("51208", "61575");
    }

    public static String parseFormParams(Map<String, Object> params) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            builder.append("&")
                    .append(entry.getKey())
                    .append("=")
                    .append(entry.getValue().toString());
        }

        if (builder.indexOf("&") == 0) {
            builder.replace(0, 1, "");
        }
        return builder.toString();

    }

    public static String parseFormParams(Map<String, Object> params, boolean ignoreNull) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            if (!ignoreNull || !Strings.isNullOrEmpty(entry.getValue().toString())) {
                builder.append("&")
                        .append(entry.getKey())
                        .append("=")
                        .append(entry.getValue().toString());
            }
        }

        if (builder.indexOf("&") == 0) {
            builder.replace(0, 1, "");
        }
        return builder.toString();

    }

    public static Map<String, String> convertUrlParamsToMap(String urlParams) {
        Map<String, String> result = Maps.newHashMap();
        String[] params = urlParams.split("&");
        for (String param : params) {
            String[] entry = param.split("=");
            if (entry.length == 2) {
                result.put(entry[0], entry[1]);
            }
        }
        return result;
    }

    private static Headers parseHeaders(Map<String, String> headers) {
        Headers.Builder builder = new Headers.Builder();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            builder.add(entry.getKey(), entry.getValue());
        }

        return builder.build();
    }

    public static void main(String[] args) {
//        Map<String, Object> params = Maps.newHashMap();
//        System.out.println(parseParams(params));
//        params.put("abc", "ddd");
//        System.out.println(parseParams(params));
//        Map<String, Object> sub = Maps.newHashMap();
//        sub.put("ccc", "ddd");
//        params.put("bbb", sub);
//        System.out.println(parseParams(params));
        TestParam testParam = new TestParam();
        testParam.setId("dad");
        testParam.setName("bbb");
        testParam.setGirlList(JSONUtil.jsonEncode(Lists.newArrayList("dddd", "eeee", "ffff")));
        System.out.println(OkHttpHelper.parsePostParams(testParam));

        System.out.println(convertUrlParamsToMap("id=123&name=&nick=sdf"));

        System.out.println(parsePostXMLParams(testParam));

//        Map<String, Object> params1 = Maps.newHashMap();
//        params1.put("uid", 1);
//        params1.put("authCookie", 123);
//        params1.put("sid", 123);
//        params1.put("code", 123);
//        String response = get("http://120.27.6.10:8349/bank/api/i/atm/info", params1);
//        System.out.println(response);

    }

    @Data
    static class TestParam {

        @HttpParameter
        String id;

        @HttpParameter(name = "myName")
        String name;

        @HttpParameter(name = "list")
        String girlList;
    }

    /**
     * URL 编码, Encode默认为UTF-8.
     *
     * @param input 需要编码的字节数组
     * @return 编码后的字符串
     */
    public static String urlEncode(String input) {
        try {
            return URLEncoder.encode(input, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException("Unsupported Encoding Exception", e);
        }
    }

}
